class Brain {
  private Brush brush;
  private Field fd;
  private int shadedCell;
  private Solver solv;
  private TPoint p;
  private TMatrix fire = genMatrix(4);
  private ArrayList squares = new ArrayList();
  private int c = 0;
  
  
  public Brain(int shaded) {
    this.shadedCell=shaded;
    this.brush=new Brush(shadedCell);
    solv = new Solver();
  }
  
  
  public Brush getBrush() {
    return this.brush;
  }
  
  
  private void setNull(Field f) {
    for (int i=0;i<f.getCount();i++) {
      for (int j=0;j<f.getCount();j++) {
        if (f.getCellValue(i, j) == 21) {
          f.setCellValue(i, j, 0);
        }
        if (f.getCellValue(i, j) == 20) {
          f.setCellValue(i, j, 1);
        }
      }
    }
  }

 
  public String getCurrentState (Field f, int i, int j) {
    switch (f.getCellValue(i, j)) {
    case -2:
      return "d";
    case 0: 
      return "a";
    case 11: 
      return "b";
    case 12: 
      return "c";
    } 

    return null;
  } 
  
  
  public TPoint getShot() {
    return (new TPoint((int)random(10), (int)random(10)));
  }
  
  
  public ArrayList getStreamConditions(Field f, int i, int j) {
    ArrayList result = new ArrayList();
    switch (f.getCellValue(i+1, j)) {
    case -1: 
      result.add("f1");
      break;
    case 12: 
      result.add("e1");
      break;
    }
    switch (f.getCellValue(i-1, j)) {
    case -1: 
      result.add("f2");
      break;
    case 12: 
      result.add("e2");
      break;
    }
    switch (f.getCellValue(i, j-1)) {
    case -1: 
      result.add("f3");
      break;
    case 12: 
      result.add("e3");
      break;
    }
    switch (f.getCellValue(i, j+1)) {
    case -1: 
      result.add("f4");
      break;
    case 12: 
      result.add("e4");
      break;
    }
    return result;
  }  
  
  
  private void setField(Field f) {
    this.fd = f;
    renderField(this.fd);
  }
  
  
  public Field getField() {
    return this.fd;
  }
  
  
  public Field perfomStreamEvent(Field f, ArrayList evt) {
    for (int i=0;evt.size() > i; i++) { 
      if (!evt.get(i).equals("x9") && !evt.get(i).equals("x10") && !evt.get(i).equals("x11") && !evt.get(i).equals("x12")) {
        this.brush.setColorCell(21);
        f = eventManager(getNum((String)evt.get(i)));
      }
      else {
        this.brush.setColorCell(1);
        f = eventManager(getNum((String)evt.get(i)));
      }
    }
    setNull(f);
    return f;
  }


  private int getNum(String s) {
    return (Integer) tableEncoding().get(s);
  }

  private Field setMarkUp(Field f) {
    for (int i=0;i < f.getCount();i++) {
      for (int j=0;j<f.getCount();j++) {

        switch(f.getCellValue(i, j)) {
        case 0: 
          f.setCellValue(i, j, 20);
          break;
        case -2:
          f.setCellValue(i, j, 0);
          break;
        }
      }
    }
    return f;
  }
  private void renderField(Field f) {
    f=new Field(f);
    f=setMarkUp(f);
    this.brush.setField(f);
    for (int i=0;i<f.getCount();i++) {
      for (int j=0;j<f.getCount();j++) {
        this.p=new TPoint(j, i);
        f=perfomStreamEvent(f, solv.getCalcRule(getCurrentState(this.fd, j, i), getStreamConditions(this.fd, j, i)));
      }
    }
    this.fd=f;
  }


  private HashMap tableEncoding() {
    HashMap command = new HashMap();
    command.put("z", 1);
    command.put("y", 2);
    command.put("x1", 3);
    command.put("x2", 4);
    command.put("x3", 5);
    command.put("x4", 6);
    command.put("x5", 7);
    command.put("x6", 8);
    command.put("x7", 9);
    command.put("x8", 10);
    command.put("x9", 3);
    command.put("x10", 4);
    command.put("x11", 5);
    command.put("x12", 6);
    command.put("w1", 11);
    command.put("w2", 12);
    command.put("w3", 13);
    command.put("w4", 14);
    return command;
  }
  // РґРµСЃРїРµС‚С‡РµСЂ СЃРѕР±С‹С‚РёР№ 
  private Field eventManager(int key) {
    switch(key) {
    case 1:
      brush.noAction(p);
      break;
    case 2:
      brush.paintCurrentCell(p);
      break; 
    case 3:
      brush.paintRightCell(p);
      break; 
    case 4:
      brush.paintLeftCell(p);
      break; 
    case 5:
      brush.paintTopCell(p);
      break; 
    case 6:
      brush.paintDownCell(p);
      break; 
    case 7:
      brush.paintTopRightCell(p);
      break; 
    case 8:
      brush.paintTopLeftCell(p);
      break; 
    case 9:
      brush.paintDownRightCell(p);
      break; 
    case 10:
      brush.paintDownLeftCell(p);
      break; 
    case 11:
      brush.paintSegmentRight(p);
      break; 
    case 12:
      brush.paintSegmentLeft(p);
      break; 
    case 13:
      brush.paintSegmentTop(p);
      break; 
    case 14:
      brush.paintSegmentDown(p);
      break;
    }
    return brush.getShadedField();
  }
  public int genCell(ArrayList row) {
    int index = int(random(row.size()));
    return index;
  }

  public TMatrix genMatrix(int n) {
    TMatrix matrix = new TMatrix(n);
    int[] row = new int[n];
    for (int i = 0 ; i < n ; i++) {
      row[i] = i;
    }

    for (int i = 0 ; i < n * 2 ; i++) {
      int k1 = int(random(n));
      int k2 = int(random(n));

      int value = row[k1];

      row[k1] = row[k2];
      row[k2] = value;
    } 

    for (int i = 0 ; i < n ; i++) {
      for (int j = 0 ; j < n ; j++) {
        if (j == row[i]) {
          matrix.setMatrixValue(i, j, 1);
        }
      }
    }

    return matrix;
  }

  private boolean checkSquare(TMatrix matrix, int row, int col, int n) {
    boolean result = true;
    for (int i = row ; i < row + n ; i++) {
      for (int j = col ; j < col + n; j++) {
        if (matrix.getMatrixValue(i, j) == 0) {
          result = false;
        }
      }
    }
    return result;
  }

  public ArrayList getSquare(TMatrix matrix, int n) {
    ArrayList squares = new ArrayList();
    boolean b = true; 
    for (int i = 0 ; i < 11 - n ; i++) {
      for (int j = 0 ; j < 11 - n ; j++) {
        if (checkSquare(matrix, i, j, n)) {
          TMatrix m = new TMatrix(n);
          TPoint p = new TPoint(i, j);
          m.setCorn(p);
          squares.add(m);
          for (int k = i ; k < i + n ; k++) {
            for (int l = j ; l < j + n; l++) {
              matrix.setMatrixValue(k, l, 0);
            }
          }
        }
      }
    }
    return squares;
  }

  private TMatrix getVer(TMatrix matrix, int n, boolean inj) {
    int count = 0;
    boolean cond = true;
    TMatrix result = new TMatrix(matrix.getDimension());  
    if (n == 1 || inj) {
      result = new TMatrix(matrix);
    }
    else {
      for (int k = 0 ; k < matrix.getDimension() ; k++) {
        for (int l = 0 ; l < matrix.getDimension() ; l++) {

          for (int i = l - n + 1; i <= l; i++) {
            for (int j = i; j < i + n; j++) {   
              if (j < 0 || j > matrix.getDimension() - 1 || matrix.getMatrixValue(k, j) == 0) {
                cond = false;
              }
            }
            if (cond) {
              count = count + 1;
            } 
            cond = true;
          }

          for (int i = k - n + 1; i <= k; i++) {
            for (int j = i; j < i + n; j++) { 
              if (j < 0 || j > matrix.getDimension() - 1 || matrix.getMatrixValue(j, l) == 0) {
                cond = false;
              }
            }
            if (cond) {
              count = count + 1;
            } 
            cond = true;
          }
          result.setMatrixValue(k, l, count);
          count = 0;
        }
      }
    }



    return result;
  }

  public TPoint getShoot(TMatrix matrix, int l, boolean inj) {    
    TMatrix result = new TMatrix(matrix.getDimension());
    ArrayList maximal = new ArrayList();
    int maximum = 0;


    for (int i = 0; i < matrix.getDimension(); i++) {
      for (int j = 0; j < matrix.getDimension(); j++) {
        maximal.add(new TPoint(i, j));
      }
    } 
    for (int i = l; i > 0; i--) {
      if (maximal.size() != 1) {
        result.addition(getVer(matrix, i, inj));
        maximum = result.getMax();
        TMatrix m = getVer(matrix, i, inj);
        for (int j = maximal.size() - 1; j > -1; j--) {
          TPoint point = (TPoint)maximal.get(j);
          if (result.getMatrixValue(point.getX(), point.getY()) != maximum) {
            maximal.remove(j);
          }
        }
      }
    }
    return ((TPoint)maximal.get((int)random(maximal.size())));
  }


  private void refreshSquares(TMatrix matrix) {
    boolean cond = true;
    for (int i = squares.size() - 1; i >= 0; i--) {
      TMatrix m = (TMatrix)squares.get(i);
      for (int j = 0; j < m.getDimension(); j++) {
        for (int k = 0; k < m.getDimension(); k++) {
          if (m.getMatrixValue(j, k) == 0 && matrix.getMatrixValue(j + m.getCorn().getX(), k + m.getCorn().getY()) == 0 ) {
            m.setMatrixValue(j, k, 1);
          }
          if (m.getMatrixValue(j, k) == 0 && fire.getMatrixValue(j, k) == 1) {
            cond = false;
          }
        }
      }
      if (cond) {
        squares.remove(i);
      }
      cond = true;
    }
  } 



  private TPoint shootSquare(TMatrix matrix) {
    TPoint shoot = null;
    if (matrix.getDimension() == 1) {
      fire = new TMatrix(1);
      fire.setMatrixValue(0, 0, 1);
    }

    for (int i = 0; i < matrix.getDimension(); i++) {
      for (int j = 0; j < matrix.getDimension(); j++) {
        if (matrix.getMatrixValue(i, j) == 0 && fire.getMatrixValue(i, j) == 1) {
          return (new TPoint(i + matrix.getCorn().getX(), j + matrix.getCorn().getY()));
        }
      }
    }
    return shoot;
  }


  public TPoint shoot(Field field, int count, boolean isInjured) {
    TMatrix matrix = field.getMatrix();
    TPoint shoot = new TPoint(-1, -1);
    setField(field);
    field=getField();
    matrix = field.getInvertedMatrix();

    if (c != count) {
      squares.clear();
      squares = getSquare(new TMatrix(matrix), count);
      fire = genMatrix(count);
      c = count;
    }
    if (!isInjured) {
      refreshSquares(matrix);
    }
    if (squares.size() == 0 || isInjured || count == 1) {
      shoot = getShoot(matrix, count, isInjured);
    }
    else {
      shoot = shootSquare((TMatrix)squares.get(0));
    }
    return shoot;
  }
} 

